// $Id: component.hpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#ifndef XPCOM_COMPONENT_HPP
#define XPCOM_COMPONENT_HPP

#include <xpcom/config.hpp>
#include <xpcom/ptr.hpp>
#include <xpcom/type_info.hpp>

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_PREFIX
#endif

class nsIComponentManager;
class nsIServiceManager;

namespace xpcom {

namespace component {

//
// Component creation classes, for ptr_factory use.
//
// Member variables has no reference hold, so use them in one expression,
// and never save it for later use.
//

// get_class_object*
class XPCOM_DECL get_class_object
{
public:
    explicit get_class_object(
            const uuid& clsid, nsIComponentManager* manager = 0) :
        clsid_(clsid),
        manager_(manager)
    {
    }

    XPCOM_METHOD operator()(const uuid& iid, void** obj) const throw();

private:
    const uuid& clsid_;
    nsIComponentManager* manager_;
};

class XPCOM_DECL get_class_object_by_contract
{
public:
    explicit get_class_object_by_contract(
            const char* contractid, nsIComponentManager* manager = 0) :
        contractid_(contractid),
        manager_(manager)
    {
    }

    XPCOM_METHOD operator()(const uuid& iid, void** obj) const throw();

private:
    const char* contractid_;
    nsIComponentManager* manager_;
};

// create_instance*
class XPCOM_DECL create_instance
{
public:
    explicit create_instance(
            const uuid& clsid,
            interface* delegate = 0, nsIComponentManager* manager = 0) :
        clsid_(clsid),
        delegate_(delegate),
        manager_(manager)
    {
    }

    XPCOM_METHOD operator()(const uuid& iid, void** obj) const throw();

private:
    const uuid& clsid_;
    interface* delegate_;
    nsIComponentManager* manager_;
};

class XPCOM_DECL create_instance_by_contract
{
public:
    explicit create_instance_by_contract(
            const char* contractid,
            interface* delegate = 0, nsIComponentManager* manager = 0) :
        contractid_(contractid),
        delegate_(delegate),
        manager_(manager)
    {
    }

    XPCOM_METHOD operator()(const uuid& iid, void** obj) const throw();

private:
    const char* contractid_;
    interface* delegate_;
    nsIComponentManager* manager_;
};

// get_service*
class XPCOM_DECL get_service
{
public:
    explicit get_service(
            const uuid& clsid, nsIServiceManager* manager = 0) :
        clsid_(clsid),
        manager_(manager)
    {
    }

    XPCOM_METHOD operator()(const uuid& iid, void** obj) const throw();

private:
    const uuid& clsid_;
    nsIServiceManager* manager_;
};

class XPCOM_DECL get_service_by_contract
{
public:
    explicit get_service_by_contract(
            const char* contractid, nsIServiceManager* manager = 0) :
        contractid_(contractid),
        manager_(manager)
    {
    }

    XPCOM_METHOD operator()(const uuid& iid, void** obj) const throw();

private:
    const char* contractid_;
    nsIServiceManager* manager_;
};

//
// Class objects helper class.
//
struct class_objects_t
{
    ptr_factory<get_class_object> operator[](const uuid& clsid) const
    {
        return make_ptr_factory(get_class_object(clsid));
    }

    ptr_factory<get_class_object_by_contract> operator[](
            const char* contractid) const
    {
        return make_ptr_factory(get_class_object_by_contract(contractid));
    }

    ptr_factory<get_class_object> operator()(
            const uuid& clsid,
            noref_ptr<nsIComponentManager> manager = 0) const
    {
        return make_ptr_factory(get_class_object(clsid, manager.get()));
    }

    ptr_factory<get_class_object_by_contract> operator()(
            const char* contractid,
            noref_ptr<nsIComponentManager> manager = 0) const
    {
        return make_ptr_factory(
                get_class_object_by_contract(contractid, manager.get()));
    }
};

//
// Components helper class.
//
struct components_t
{
    ptr_factory<create_instance> operator[](const uuid& clsid) const
    {
        return make_ptr_factory(create_instance(clsid));
    }

    ptr_factory<create_instance_by_contract> operator[](
            const char* contractid) const
    {
        return make_ptr_factory(create_instance_by_contract(contractid));
    }

    ptr_factory<create_instance> operator()(
            const uuid& clsid,
            noref_ptr<interface> delegate = 0,
            noref_ptr<nsIComponentManager> manager = 0) const
    {
        return make_ptr_factory(
                create_instance(clsid, delegate.get(), manager.get()));
    }

    ptr_factory<create_instance_by_contract> operator()(
            const char* contractid,
            noref_ptr<interface> delegate = 0,
            noref_ptr<nsIComponentManager> manager = 0) const
    {
        return make_ptr_factory(create_instance_by_contract(
                        contractid, delegate.get(), manager.get()));
    }
};

//
// Services helper class.
//
struct services_t
{
    ptr_factory<get_service> operator[](const uuid& clsid) const
    {
        return make_ptr_factory(get_service(clsid));
    }

    ptr_factory<get_service_by_contract> operator[](
            const char* contractid) const
    {
        return make_ptr_factory(get_service_by_contract(contractid));
    }

    ptr_factory<get_service> operator()(
            const uuid& clsid,
            noref_ptr<nsIServiceManager> manager = 0) const
    {
        return make_ptr_factory(get_service(clsid, manager.get()));
    }

    ptr_factory<get_service_by_contract> operator()(
            const char* contractid,
            noref_ptr<nsIServiceManager> manager = 0) const
    {
        return make_ptr_factory(
                get_service_by_contract(contractid, manager.get()));
    }

    static XPCOM_DECL bool is_instantiated(
            const uuid& clsid, const uuid& iid,
            noref_ptr<nsIServiceManager> manager = 0);

    static XPCOM_DECL bool is_instantiated(
            const char* contractid, const uuid& iid,
            noref_ptr<nsIServiceManager> manager = 0);

    template <typename T>
    static bool is_instantiated(
            const uuid& clsid,
            noref_ptr<nsIServiceManager> manager = 0)
    {
        return is_instantiated(clsid, typeid_of<T>(), manager);
    }

    template <typename T>
    static bool is_instantiated(
            const char* contractid,
            noref_ptr<nsIServiceManager> manager = 0)
    {
        return is_instantiated(contractid, typeid_of<T>(), manager);
    }
};

const class_objects_t class_objects = {};
const components_t components = {};
const services_t services = {};

} // namespace xpcom::component

//
// class_objects/components/services can be used in different ways.
//
// First, in most case, use the operator[] will be ok. Use ref_ptr to get the
// result.
//
//  ref_ptr<IFoo> foo = components[clsid];
//  ref_ptr<IBar> bar = services["contractid"];
//
// Second, operator() can be used with more arguments, for instance, you need
// to create aggregated component, or have component/service manager instance
// present.
//
//  ref_ptr<IFoo> foo = components(clsid, delegate);
//  ref_ptr<IBar> bar = class_objects(clsid, component_manager);
//
// Finally, you can also access the raw XPCOM method of creating things, but
// normally you don't want to do this:
//  result r = components[a_clsid](iid, received_object);
//
// Note: you can call services.is_instantiated(...) to check if the service is
//       instantiated.
//
using component::class_objects;
using component::components;
using component::services;

} // namespace xpcom

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_SUFFIX
#endif

#endif // XPCOM_COMPONENT_HPP
